﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using JetBlack.IO;

namespace JetBlack.Net.Sockets
{
    internal class NonBlockingReader
    {
        public readonly Socket Socket;
        private readonly Queue<byte[]> queue = new Queue<byte[]>();
        private byte[] buffer = null;
        private readonly byte[] len = new byte[4];
        private int offset = 0;

        public NonBlockingReader(Socket socket)
        {
            Socket = socket;
        }

        public bool IsEmpty
        {
            get { lock (this) { return queue.Count == 0; } }
        }

        public byte[] Pop()
        {
            lock (this) { return queue.Dequeue(); }
        }

        public bool Read()
        {
            lock (this)
            {
                int count = queue.Count;

                if (buffer == null)
                {
                    offset += Read(Socket, len, offset);

                    if (offset == len.Length)
                    {
                        offset = 0;
                        buffer = new byte[NetworkBitConverter.ToInt32(len, 0)];
                    }
                }

                if (buffer != null)
                {
                    offset += Read(Socket, buffer, offset);

                    if (offset == buffer.Length)
                    {
                        queue.Enqueue(buffer);
                        offset = 0;
                        buffer = null;
                    }
                }

                return queue.Count > count;
            }
        }

        static private int Read(Socket socket, byte[] buf, int offset)
        {
            SocketError errorCode;
            int bytesRead = socket.Receive(buf, offset, buf.Length - offset, SocketFlags.Partial, out errorCode);

            if (!IsValidRead(errorCode, bytesRead))
                throw new System.IO.EndOfStreamException();

            return bytesRead;
        }

        static private bool IsValidRead(SocketError errorCode, int bytesRead)
        {
            return
                errorCode == SocketError.WouldBlock ||
                (errorCode == SocketError.Success && bytesRead > 0);
        }
    }
}
